import re
from types import Struct
import collections
from gate import Gate
from stencils import *
import sys
import tracer
import os
import time

class RoutingException(Exception):
	pass

class Route:
	def __init__(self, path, controller, settings = {}):
		self.path = [(pattern, re.compile('^' + pattern + '$'), name) for pattern, name in path.items()]
		self.controller = controller
		self.settings = settings

	def match(self, reqPath):
		i = 0
		reqPathLen = len(reqPath)

		args = {}

		print 'Trying to route %s' % self.controller

		for (orig, pattern, name) in self.path:
			if i >= reqPathLen:
				return False
			match = pattern.findall(reqPath[i])
			if len(match) != 1:
				return False

			if name is None:
				name = 'item%s'%i

			args[name] = match[0]

			i += 1

		# Save extra params
		args['extra'] = []
		extraStart = i
		for i in range(i, len(reqPath)):
			args['extra'].append(reqPath[i])

		print 'Resolved args: %s' % args

		return (self.controller, args, self.settings)

		print '-'*80

class ErrorRoute:
	def __init__(self, code, controller, settings = []):
		self.code = code
		self.controller = controller
		self.settings = settings

class Router:
	def __init__(self, routes):
		self.routes = []
		self.errors = {} # 403: 'NotFoundStencil', 404: 'PermissionDeniedStencil'}
		self.controllers = {}
		for route in routes:
			if route.__class__ == Route:
				print 'Route'
				self.routes.append(route)
			elif route.__class__ == ErrorRoute:
				print 'Error'
				self.errors[route.code] = route
			else:
				raise Exception('Unexpected route part, expecting Route or ErrorRoute!')
		pass

	def _exec(self, root, controller, env, settings):
		print 'Executing controller %s' % controller
		try:
			if type(controller) is str:
				controllerName = root + '/' + controller + '.py'
				if not os.path.exists(controllerName):
					raise RoutingException(404)
				mtime = time.ctime(os.path.getmtime(controllerName))

				if (not (controllerName in self.controllers)) or (self.controllers[controllerName]['changed'] < mtime):
					print 'File `%s` has changed on %s or was not loaded yet, reloading' % (controllerName, mtime)
					mod = __import__(controller)
					reload(mod)
					main = mod.Main()
					if 'setup' in dir(mod.Main):
						main.setup(settings)
					self.controllers[controllerName] = {'instance':main, 'changed':mtime}
				else:
					main = self.controllers[controllerName]['instance']
			else:
				main = controller.Main()
				if 'setup' in dir(controller.Main):
					main.setup(settings)

			gate = Gate(env)
			return main.execute(gate)
		except RoutingException:
			raise
		except:
			return self._exec(root, tracer, env, [])

	def route(self, root, reqPath, env):
		if type(reqPath) is str:
			reqPath = reqPath.strip('./ ');
			if len(reqPath) == 0:
				reqPath = ['index']
			else:
				reqPath = filter(None, reqPath.split('/'))

		print reqPath

		try:
			for route in self.routes:
				result = route.match(reqPath)
				if result:
					env['args'] = result[1]
					res = self._exec(root, result[0], env, result[2])
					if res:
						return
			raise RoutingException(404)
		except RoutingException:
			code = int(str(sys.exc_info()[1]))
			if code in self.errors:
				self._exec(root, self.errors[code].controller, env, self.errors[code].settings)
			else:
				raise RoutingException('No route for error %s' % str(code))
		except:
			raise
